home *** CD-ROM | disk | FTP | other *** search
- # ui_gtk.py - graphical user interface implemented using GTK+
- # Copyright (C) 2008, 2009 Canonical, Ltd.
- #
- # This program is free software: you can redistribute it and/or modify
- # it under the terms of the GNU General Public License as published by
- # the Free Software Foundation, version 3 of the License.
- #
- # This program is distributed in the hope that it will be useful,
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- # GNU General Public License for more details.
- #
- # You should have received a copy of the GNU General Public License
- # along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-
- import logging
- import os
- import sys
- import threading
- import time
- import traceback
-
- import computerjanitor
- import computerjanitorapp
- _ = computerjanitorapp.setup_gettext()
-
- # We can't import gtk here, since that would mean it gets imported
- # even when we run in command line mode. Thus, we do the imports in
- # the class, when we know we need them.
-
-
- import os
- GLADE = os.environ.get("COMPUTER_JANITOR_GLADE",
- "/usr/share/computer-janitor/ComputerJanitor.glade")
-
-
- NAME_COL = 0
- STATE_COL = 1
- CRUFT_COL = 2
-
-
- class ListUpdater(threading.Thread):
-
- def __init__(self, ui):
- threading.Thread.__init__(self)
- self.ui = ui
-
- def run(self):
- crufts = self.find_cruft()
- crufts = self.sort_cruft(crufts)
- self.ui.do(self.ui.update_progressbar, "", 0.0)
- self.ui.do(self.ui.update_cruft_list, crufts)
-
- def plugin_find_cb(self, filename, index, count):
- self.ui.do(self.ui.update_progressbar, _("Finding plugins"),
- float(index+1) / count)
-
- def find_cruft(self):
- list = []
- plugins = self.ui.pm.get_plugins(callback=self.plugin_find_cb)
- for i in range(len(plugins)):
- self.ui.do(self.ui.update_progressbar, _("Finding problems"),
- float(i+1) / len(plugins))
-
- for cruft in plugins[i].get_cruft():
- list.append(cruft)
-
- return self.ui.app.remove_whitelisted(list)
-
- def sort_cruft(self, crufts):
- # Sort crufts: First all unignored cruft, then all ignored cruft.
- # In each section first everything else, then files to be removed,
- # then packages to be removed.
-
- def key(cruft):
- name = cruft.get_name()
- if isinstance(cruft, computerjanitor.PackageCruft):
- kind_class = 3
- elif isinstance(cruft, computerjanitor.FileCruft):
- kind_class = 2
- else:
- kind_class = 1
- return (not self.ui.app.state.is_enabled(name),
- kind_class, name)
-
- list = [(key(cruft), cruft) for cruft in crufts]
- list.sort()
- return [cruft for k, cruft in list]
-
-
- class Cleaner(threading.Thread):
-
- def __init__(self, ui, crufts, plugins):
- threading.Thread.__init__(self)
- self.ui = ui
- self.crufts = crufts
- self.plugins = plugins
-
- def run(self):
- for cruft in self.crufts:
- self.ui.do(self.ui.pulse_progressbar, _("Cleaning up"))
- name = cruft.get_name()
- if self.ui.app.state.is_enabled(name):
- if self.ui.options.no_act:
- logging.info(_("Pretending to clean up: %s") % name)
- else:
- logging.info(_("Cleaning up: %s") % name)
- cruft.cleanup()
-
- for plugin in self.plugins:
- self.ui.do(self.ui.pulse_progressbar, _("Post-cleanup"))
- if self.ui.options.no_act:
- logging.info(_("Pretending to post-cleanup: %s") % plugin)
- else:
- logging.info(_("Post-cleanup: %s") % plugin)
- error = None
- try:
- plugin.post_cleanup()
- except Exception, e:
- logging.debug(unicode(traceback.format_exc()))
- self.ui.do(self.ui.idle_error_message,
- _("Could not clean up properly"),
- unicode(e))
- break
-
- self.ui.show_cruft()
- self.ui.do(self.ui.button_cleanup.set_sensitive, True)
-
-
- class GtkUserInterface(computerjanitorapp.UserInterface):
-
- def update_progressbar(self, msg, fraction):
- self.progressbar.set_text(msg)
- self.progressbar.set_fraction(fraction)
-
- def pulse_progressbar(self, msg):
- self.progressbar.set_text(msg)
- self.progressbar.pulse()
-
- def update_sensitive(self):
- sensitive = False
- iter = self.liststore.get_iter_first()
- while not sensitive and iter:
- sensitive = self.liststore.get_value(iter, STATE_COL)
- iter = self.liststore.iter_next(iter)
-
- self.button_cleanup.set_sensitive(sensitive)
-
- def show_cruft(self, *args):
- updater = ListUpdater(self)
- updater.start()
-
- def cleanup(self, *args):
- self.button_cleanup.set_sensitive(False)
-
- crufts = []
- iter = self.liststore.get_iter_first()
- while iter:
- crufts.append(self.liststore.get_value(iter, CRUFT_COL))
- iter = self.liststore.iter_next(iter)
-
- packages = [cruft
- for cruft in crufts
- if (isinstance(cruft, computerjanitor.PackageCruft) and
- self.app.state.is_enabled(cruft.get_name()))]
-
- msg = _("Really clean up?")
- dialog = self.gtk.MessageDialog(parent=self.window_main,
- type=self.gtk.MESSAGE_WARNING,
- buttons=self.gtk.BUTTONS_YES_NO,
- message_format=msg)
-
- if packages:
- msg = (_("You are <b>removing %d .deb packages.</b> "
- "This may break your system, if you need them. "
- "Do you want to continue?") %
- len(packages))
- else:
- msg = _("Do you want to continue?")
- dialog.format_secondary_markup(msg)
-
- dialog.show_all()
- response = dialog.run()
- dialog.hide()
- if response != self.gtk.RESPONSE_YES:
- self.update_sensitive()
- return
-
- cleaner = Cleaner(self, crufts, self.pm.get_plugins())
- cleaner.start()
-
- def close(self, *args):
- self.gtk.main_quit()
-
- def cruft_toggled(self, toggle, path):
- iter = self.liststore.get_iter(path)
-
- cruft = self.liststore.get_value(iter, CRUFT_COL)
- cruft_name = cruft.get_name()
- enabled = self.liststore.get_value(iter, STATE_COL)
-
- enabled = not enabled
- if enabled:
- self.app.state.enable(cruft_name)
- else:
- self.app.state.disable(cruft_name)
- self.app.state.save(self.options.state_file)
- self.liststore.set_value(iter, STATE_COL, enabled)
-
- self.update_sensitive()
-
- def translate_description(self, cruft):
- # Ugly hack to get translations to work; see LP: #361312.
- desc = cruft.get_description()
- import computerjanitor
- if isinstance(cruft, computerjanitor.PackageCruft):
- desc1, desc2 = desc.split("\n\n", 2)
- if desc1 and desc2:
- desc = "%s\n\n%s" % (_(desc1), desc2)
- elif isinstance(cruft, computerjanitor.FileCruft):
- if desc.endswith("\n"):
- desc = desc[:-1]
- return desc
-
- def cursor_changed(self, treeview):
- selection = treeview.get_selection()
- model, rows = selection.get_selected_rows()
- if len(rows) == 1:
- iter = model.get_iter(rows[0])
- cruft = model.get_value(iter, CRUFT_COL)
- desc = self.translate_description(cruft)
- self.cruft_description.set_label(desc)
- self.do(self.scroll_to_selection)
-
- def scroll_to_selection(self):
- selection = self.treeview_cruft.get_selection()
- model, rows = selection.get_selected_rows()
- if len(rows) == 1:
- iter = model.get_iter(rows[0])
- path = model.get_path(iter)
- self.treeview_cruft.scroll_to_cell(path)
-
- def mapped(self, *args):
- if os.getuid() != 0:
- dialog = self.error_dialog(_("Root access required."),
- _("You must run computer-janitor-gtk "
- "as root. Sorry."))
- dialog.show()
- dialog.run()
- sys.exit(1)
-
- try:
- self.app.verify_apt_cache()
- except computerjanitor.Exception, e:
- logging.debug(unicode(traceback.format_exc()))
- dialog = self.error_dialog(str(e))
- dialog.show()
- dialog.run()
- sys.exit(1)
-
- self.show_cruft()
- self.window_main.disconnect(self.mapped_id)
-
- def format_name(self, cruft):
- name = self.gobject.markup_escape_text(cruft.get_shortname())
- desc = cruft.get_prefix_description() or cruft.get_prefix()
- desc = self.gobject.markup_escape_text(desc)
- return ("<b>%s</b> (%s)" % (name, desc))
-
- def update_cruft_list(self, crufts):
- self.liststore.clear()
- self.cruft_description.set_text("")
- for cruft in crufts:
- dict = {
- NAME_COL: self.format_name(cruft),
- STATE_COL: self.app.state.is_enabled(cruft.get_name()),
- CRUFT_COL: cruft,
- }
- list = [dict[i] for i in sorted(dict.keys())]
- self.liststore.append(list)
- self.update_sensitive()
-
- def error_dialog(self, msg, secondary_msg=None):
- dialog = self.gtk.MessageDialog(parent=self.window_main,
- type=self.gtk.MESSAGE_ERROR,
- buttons=self.gtk.BUTTONS_OK,
- message_format=msg)
- if secondary_msg:
- dialog.format_secondary_text(secondary_msg)
-
- return dialog
-
- def idle_error_message(self, msg, secondary_msg=None):
- dialog = self.error_dialog(msg, secondary_msg)
- dialog.connect("response", lambda d=dialog, *args: d.destroy())
- dialog.show()
-
- def do(self, callable, *args):
- def helper():
- callable(*args)
- return False
- self.gobject.idle_add(helper)
-
- def run(self, options, args):
- import gtk
- import gtk.glade
- import gobject
-
- locpath = os.environ.get("LOCPATH", "/usr/share/locale")
- gtk.glade.bindtextdomain("computerjanitor", locpath)
- gtk.glade.textdomain("computerjanitor")
-
- self.options = options
- self.gtk = gtk
- self.gobject = gobject
-
- self.app.state.load(options.state_file)
-
- self.glade = gtk.glade.XML(GLADE)
- self.window_main = self.glade.get_widget("window_main")
- self.treeview_cruft = self.glade.get_widget("treeview_cruft")
- self.cruft_description = self.glade.get_widget("cruft_description")
- self.button_cleanup = self.glade.get_widget("button_cleanup")
-
- self.mapped_done = False
-
- # connect to signals (we do this manually for now)
- todo = [
- ("button_cleanup", "clicked", self.cleanup, None),
- ("button_close", "clicked", self.close, None),
- ("window_main", "delete-event", self.close, None),
- ("window_main", "map-event", self.mapped, "mapped_id"),
- ("treeview_cruft", "cursor-changed", self.cursor_changed, None),
- ]
- for widget_name, signal_name, callback, attr in todo:
- widget = self.glade.get_widget(widget_name)
- id = widget.connect(signal_name, callback)
- if attr:
- setattr(self, attr, id)
-
- # create the column with the toggle
- col = gtk.TreeViewColumn(_('Remove/fix?'))
- cr = gtk.CellRendererToggle()
- cr.connect("toggled", self.cruft_toggled)
- col.pack_start(cr, expand=False)
- col.add_attribute(cr, 'active', STATE_COL)
- self.treeview_cruft.append_column(col)
- self.treeview_cruft.set_headers_visible(True)
-
- # create the column with the name of cruft
- col = gtk.TreeViewColumn(_('Name'))
- cr = gtk.CellRendererText()
- col.pack_start(cr)
- col.add_attribute(cr, 'markup', NAME_COL)
- self.treeview_cruft.append_column(col)
-
- # create the store
- self.liststore = gtk.ListStore(str, 'gboolean', object)
-
- # attach it to the view
- self.treeview_cruft.set_model(self.liststore)
-
- # setup progress bar
- self.progressbar = self.glade.get_widget("progressbar")
-
- # Display the version number.
- self.cruft_description.set_text(_("Computer Janitor %s") %
- computerjanitorapp.VERSION)
-
- # show the window
- self.window_main.show()
- gtk.gdk.threads_init()
- gtk.main()
-